﻿#include "InstrumentedTimer.h"
/*
可视化基准测试原理是将测试代码运行多次，统计运行时间，并将结果输出到文件中（比如：json），
通过可视化工具（比如谷歌（chrome://tracing）或者 Edge（edge://tracing），微软的Power BI）等对结果进行图形绘制。
*/

#if defined(PROFING) && PROFING
    std::mutex mtx; // 定义互斥锁全局变量，用于保护共享资源
#endif


// Instrumented 类，用于记录一个会话的基本信息
Instrumented::Instrumented() :
    m_CurrentSession(nullptr),
    m_ProfileCount(0) {}

void Instrumented::BeginSession(const std::string& sessionName, const std::string& intactFileName)
{
    // 打开输出文件流，写入头部信息 results.json 是默认文件名
    m_OutputStream.open(intactFileName);
    WriteHeader();
    m_CurrentSession = new InstrumentationSession{ sessionName };
}

void Instrumented::EndSession()
{
    WriteFooter();
    m_OutputStream.close();
    delete m_CurrentSession;
    m_CurrentSession = nullptr;
    m_ProfileCount = 0;
}

void Instrumented::SetCurrentSessionName(const std::string& sessionName)
{ // 设置当前会话名（进程名）
    if (m_CurrentSession)
        //m_CurrentSession->Name = std::to_string(std::hash<std::thread::id>()(std::this_thread::get_id())); // 获取线程 ID 的 hash 算法，转换为字符串作为进程名
        m_CurrentSession->Name = sessionName;
}

void Instrumented::WriteProfile(const ProfileResult& result)
{
    std::lock_guard<std::mutex> lock(mtx); // 加锁

    if (m_ProfileCount++ > 0)
        m_OutputStream << ",";  // 若不是第一个，则添加逗号，用于 JSON 格式多段数据

    std::string name = result.Name;
    std::replace(name.begin(), name.end(), '"', '\'');  // JSON 字符串的双引号替换为单引号

    m_OutputStream << "{";
    m_OutputStream << "\"cat\":\"function\",";          // 标记为 function 类型
    m_OutputStream << "\"dur\":" << (result.End - result.Start) << ","; // 持续时间
    m_OutputStream << "\"name\":\"" << name << "\",";   // 函数名
    m_OutputStream << "\"ph\":\"X\",";                  // 标记为 X 类型，表示这是个函数
    m_OutputStream << "\"pid\": \"" << m_CurrentSession->Name << "\","; // 进程 ID
    m_OutputStream << "\"tid\":" << result.ThreadID << ",";                     // 线程 ID
    m_OutputStream << "\"ts\":" << result.Start;        // 开始时间，时间戳
    m_OutputStream << "}";

    m_OutputStream.flush(); // 冲洗每一个配置文件，以确保正确的顺序，程序崩溃时可以避免混乱，std::ofstream.flush 函数的作用是将缓存区的内容输出到文件中
}

void Instrumented::WriteHeader()
{ // 写入配置文件的头部信息
    std::lock_guard<std::mutex> lock(mtx); // 加锁

    m_OutputStream << "{\"otherData\": {},\"traceEvents\":["; // tracing 需要的特点格式
    m_OutputStream.flush();
}

void Instrumented::WriteFooter()
{
    std::lock_guard<std::mutex> lock(mtx); // 加锁

    m_OutputStream << "]}";
    m_OutputStream.flush();
}

Instrumented& Instrumented::Singleton()
{  // 单例模式函数
    static Instrumented* instance = new Instrumented();
    return *instance;
}



// InstrumentationTimer 类，用于记录函数运行时间
InstrumentationTimer::InstrumentationTimer()
    : t_name("Timer default")
    , t_stop(false) {
    start_time_point = std::chrono::high_resolution_clock::now(); // 记录开始时间，返回值是系统时钟的时间点指针对象
}

InstrumentationTimer::InstrumentationTimer(const char* name)
    : t_name(name)
    , t_stop(false) {
    start_time_point = std::chrono::high_resolution_clock::now();
}

InstrumentationTimer::~InstrumentationTimer() {
    if (!t_stop)
        stop();
}

void InstrumentationTimer::stop() {
    auto end_time_point = std::chrono::high_resolution_clock::now();
    /*auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end_time - start_time);
    std::cout << "Time elapsed: " << duration.count() * 0.001 << " millisecond for " << t_name << std::endl; */

    /*long long start_time = std::chrono::time_point_cast<std::chrono::microseconds>(start_time_point).time_since_epoch().count(); // 记录开始时间，微秒级
    long long end_time = std::chrono::time_point_cast<std::chrono::microseconds>(end_time_point).time_since_epoch().count(); // 记录结束时间 */
    long long start_time = std::chrono::time_point_cast<std::chrono::milliseconds>(start_time_point).time_since_epoch().count(); // 记录开始时间，毫秒级
    long long end_time = std::chrono::time_point_cast<std::chrono::milliseconds>(end_time_point).time_since_epoch().count();
    std::cout << "Time elapsed: " << (end_time - start_time)/* * 0.0001*/ << " millisecond for " << t_name << std::endl; // 输出时间差

    uint32_t thread_id = std::hash<std::thread::id>()(std::this_thread::get_id()); // 获取线程 ID 使用 hash 算法配合 std::bind() 与函数调用主函数会不区分线程名
    Instrumented::Singleton().WriteProfile({ t_name, start_time, end_time, thread_id }); // 使用 Instrumented 单例，写入名字、开始和结束时间（先转换为结构体）
    t_stop = true;
}



#if defined(PROFING)
void test_progress(void func(void), std::string sessionName, const std::string& intactFileName) { // 函数指针作为参数，用于多线程测试
    static std::mutex in_mtx;                      // 互斥锁，用于线程安全
    std::lock_guard<std::mutex> lock(in_mtx);        // 加锁
    Instrumented::Singleton().BeginSession(sessionName, intactFileName);     // 开始计时，传入测试进程名称
    //MyBaseTest::RunBenchmarks();              // 运行基准测试函数
    func();                                     // 回调函数
    Instrumented::Singleton().EndSession();           // 结束计时
    //in_mtx.lock();
}
#endif

